package com.isp.common.exception.advice;


import com.isp.common.exception.filter.ContentCachingRequestWrapper;
import com.isp.common.model.ResultCode;
import com.isp.common.model.enums.SystemCodeEnum;
import com.isp.common.model.exception.APIRuntimeException;
import com.isp.common.model.exception.BusinessException;
import com.isp.common.model.vo.Notification;
import com.isp.common.model.vo.Result;
import com.isp.common.utils.JacksonUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.context.support.DefaultMessageSourceResolvable;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 异常统一处理器
 *
 * @author Linsy
 * @version 1.0
 * @created 2023/6/20 2:09 PM
 */
@ControllerAdvice
@RestController
public class ExceptionAdvice {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionAdvice.class);

    private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");

    @ExceptionHandler(value = Throwable.class)
    public Result<Object> throwableHandle(Throwable e, HttpServletRequest request) {
        String causeMessage = "";
        if (Objects.nonNull(e.getCause())) {
            causeMessage = e.getCause().getMessage();
        }
        if (Objects.nonNull(e.getCause()) && Objects.nonNull(e.getCause().getCause())) {
            causeMessage = e.getCause().getCause().getMessage();
        }
        exceptionWaring(SystemCodeEnum.SERVER_ERROR.getRet(), e, request);
        LOGGER.error("服务器开小差了:class:[{}], message:[{}], causeMessage:[{}]", e.getClass(), e.getMessage(), causeMessage, e);
        return Result.error(SystemCodeEnum.SERVER_ERROR);
    }

    @ModelAttribute
    public void getBodyInfo(HttpServletRequest request) {
        //获取requestBody
        try {
            if (request != null) {
                ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
                String body = new String(requestWrapper.getRequestBody(), request.getCharacterEncoding());
                request.setAttribute("requestBodyMessage", body);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @ExceptionHandler(value = BusinessException.class)
    public Result<ResultCode> businessExceptionHandle(BusinessException e, HttpServletRequest request) {
        String message = e.getMessage();
        if (StringUtils.isNotBlank(message)) {
            return Result.error(message);
        }
        exceptionWaring(SystemCodeEnum.ERROR.getRet(), e, request);
        return Result.error(SystemCodeEnum.ERROR);
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result<ResultCode> methodArgumentNotValidExceptionHandle(MethodArgumentNotValidException e, HttpServletRequest request) {
        List<FieldError> fieldErrors = e.getFieldErrors();
        if (CollectionUtils.isNotEmpty(fieldErrors)) {
            String message = fieldErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
            exceptionWaring(SystemCodeEnum.PARAMETER_MISSING.getRet(), e, request);
            return Result.error(message);
        }
        exceptionWaring(SystemCodeEnum.PARAMETER_MISSING.getRet(), e, request);
        return Result.error(SystemCodeEnum.PARAMETER_MISSING);
    }

    @ExceptionHandler(value = BindException.class)
    public Result<ResultCode> bindException(BindException e, HttpServletRequest request) {
        List<FieldError> fieldErrors = e.getFieldErrors();
        if (CollectionUtils.isNotEmpty(fieldErrors)) {
            String message = fieldErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
            exceptionWaring(SystemCodeEnum.PARAMETER_MISSING.getRet(), e, request);
            return Result.error(message);
        }
        exceptionWaring(SystemCodeEnum.PARAMETER_MISSING.getRet(), e, request);
        return Result.error(SystemCodeEnum.PARAMETER_MISSING);
    }

    /**
     * 处理自定义的业务异常
     *
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = APIRuntimeException.class)
    @ResponseBody
    public Result<ResultCode> bizExceptionHandler(HttpServletRequest req, APIRuntimeException e, HttpServletRequest request) {
        LOGGER.info("发生业务异常！code={}", e.getCode(), e);
        return Result.bizException(e);
    }

    /**
     * 异常预警
     *
     * @param code    异常码
     * @param e       异常信息
     * @param request 请求
     */
    private void exceptionWaring(Integer code, Throwable e, HttpServletRequest request) {
        Object body = request.getAttribute("requestBodyMessage");
        Map<Object, Object> bodyJson = new HashMap<>();
        if (Objects.nonNull(body) && !body.equals("")) {
            bodyJson = JacksonUtils.parseMap(body.toString(), Object.class, Object.class);
        }
        String hostAddress = null;
        try {
            InetAddress address = InetAddress.getLocalHost();
            hostAddress = address.getHostAddress();
        } catch (UnknownHostException ex) {
            ex.printStackTrace();
        }

        // 获取请求的URL
        String requestURL = request.getRequestURL().toString();
        // 获取请求的方法 (GET, POST, PUT)
        String requestMethod = request.getMethod();
        // 获取请求参数
        getQueryUrlObject(request.getQueryString(), bodyJson);

        Notification notification = new Notification.NotificationBuilder().time(DATE_FORMAT.format(System.currentTimeMillis()))
                .host(hostAddress).apiUrl(requestURL).method(requestMethod).request(JacksonUtils.toJsonString(bodyJson))
                .exceptionMessage(ExceptionUtils.getMessage(e)).build();
        LOGGER.error(notification.toString(), e);
    }

    private void getQueryUrlObject(String queryString, Map<Object, Object> jsonObject) {
        if (StringUtils.isNotBlank(queryString)) {
            Map<Object, Object> requestParam = new HashMap<>();
            String[] split = queryString.split("&");
            for (String str : split) {
                String[] splitEqual = str.split("=");
                if (splitEqual.length > 1) {
                    requestParam.put(splitEqual[0], splitEqual[1]);
                } else {
                    if (!"".equals(splitEqual[0])) {
                        requestParam.put(splitEqual[0], "");
                    }
                }
            }
            jsonObject.put("request_param", requestParam);
        }
    }

}
